#include <stdint.h>
#include <stdbool.h>

#include "eeprom.h"
#include "LCD1602.h"
#include "assets.h"
#include "delay.h"
#include "keyboard.h"
#include "led.h"

#define MAX_PWD_LEN 10
#define MAX_REMAIN_TRY 5
#define DELAY_TIME 20 //Wait how many secs if error count exceed
#define TIMEOUT_TIME 20 //Cancel after how many secs if no key pressed
#define MESSAGE_TIME 20 //How many secs should a message display
#define AUTOLOCK_TIME 20

void IdleState(void) {
	LCD1602_PrintAll(STR_Welcome, STR_Welcome2);
	//wait for keypress
	while (KBD_WaitKey(-1) < 0);
}

int8_t EnterStr(char code *title, char *buffer) {
	//输入一个字符串
	int8_t len = 0;
	uint8_t titlelen = 1;
	LCD1602_PrintAll(title, STR_InputPrompt);
	
	//判断title的长度
	while (*title) {
		titlelen++;
		title++;
	}
	
	for (;;) {
		int8_t key = KBD_WaitKey(TIMEOUT_TIME * CLOCK_PER_SEC);
		if (key < 0) return -1;//无操作取消
		if (key == KEY_A) break;//A键确认
		if (key == KEY_B) {
			if (len == 0) return -1;//长度为0时B键取消
			//否则B键退格
			len--;
			//erase '*'
			LCD1602_DDRamAddr(titlelen + len);
			LCD1602_DataMode();
			LCD1602_Data(' ');
			if (len == 0) {
				//更新提示语句
				LCD1602_DDRamAddr(0x40 + 5);
				LCD1602_PutStr_c(STR_Cancel);
				LCD1602_PutStr_c(STR_Space);
			}
		} else if (len < MAX_PWD_LEN) {
			//paint '*'
			LCD1602_DDRamAddr(titlelen + len);
			LCD1602_DataMode();
			LCD1602_Data('*');
			if (len == 0) {
				//更新提示语句
				LCD1602_DDRamAddr(0x40 + 5);
				LCD1602_PutStr_c(STR_Backspace);
			}
			buffer[len] = key;
			len++;
		}
	}
	return len;
}

//return:
//0 = Aborted
//1 = OK
//2 = Wrong
uint8_t EnterPWDState(void) {
	char buffer[MAX_PWD_LEN];
	int8_t ret;
	int8_t pwdlen, i;
	ret	= EnterStr(STR_Unlocking, buffer);
	if (ret < 0) {
		return 0;//取消
	}
	//Check
	pwdlen = EEP_Read(EEP_PwdLen);
	if (pwdlen != ret) {//先检查长度
		return 2;
	}
	for (i = 0; i < pwdlen; i++) {//逐字节检查
		char thischar;
		thischar = EEP_Read(EEP_Pwd + i);
		if (thischar != buffer[i]) {
			return 2;
		}
	}
	return 1;
}

void ChangePWDState(void) {
	char buffer1[MAX_PWD_LEN], buffer2[MAX_PWD_LEN];
	int8_t len1, len2;
	//先输一遍新密码
	len1 = EnterStr(STR_ChangePWD, buffer1);
	//（len < 0 -> 取消）
	if (len1 >= 0) {
		//再重复一遍新密码
		len2 = EnterStr(STR_ChangePWD2, buffer2);
		if (len2 >= 0) {
			bool OK = true;
			if (len1 != len2) {//检查长度
				OK = false;
			} else {
				uint8_t i;
				for (i = 0; i < len1; i++) {//逐字节检查
					if (buffer1[i] != buffer2[i]) {
						OK = false;
						break;
					}
				}
			}
			if (OK) {
				//Show OK
				uint8_t i;
				LCD1602_PrintAll(STR_ChangeOK, STR_None);
				EEP_Erase(EEP_GetPage(EEP_Pwd));
				EEP_Program(EEP_PwdLen, len1);
				for (i = 0; i < len1; i++) {
					EEP_Program(EEP_Pwd + i, buffer1[i]);
				}
			} else {
				//show error
				LCD1602_PrintAll(STR_ChangeFail, STR_ChangeFail2);
			}
			KBD_WaitKey(MESSAGE_TIME * CLOCK_PER_SEC);
		}
	}
}

void FormatTime(uint16_t time, char *buffer) {
	//将时间格式化为xxm yys的格式
	//或xm yys，yys，ys
	bool zero = false;
	//最多99分钟
	if (time > 5999) time = 5999;
	if (time >= 60) {
		uint8_t min = time / 60;
		if (min >= 10) {
			*buffer = '0' + (min / 10);
			buffer++;
		}
		*buffer = '0' + (min % 10);
		buffer++;
		*buffer = 'm';
		buffer++;
		*buffer = ' ';
		buffer++;
		zero = true;
	}
	{
		uint8_t sec = time % 60;
		if (zero || sec >= 10) {
			*buffer = '0' + (sec / 10);
			buffer++;
		}
		*buffer = '0' + (sec % 10);
		buffer++;
		*buffer = 's';
		buffer++;
		*buffer = '\0';
		buffer++;
	}
}

void BlockedState(void) {
	//showmsg, delay
	uint16_t t;
	LCD1602_PrintAll(STR_Blocked, STR_Blocked2);
	delay(0, false);
	for (t = DELAY_TIME; t > 0; t--) {
		char buffer[8];//12m 34s\0
		FormatTime(t, buffer);
		LCD1602_DDRamAddr(0x49);
		LCD1602_PutStr(buffer);
		LCD1602_PutStr_c(STR_Space);
		delay(CLOCK_PER_SEC, true);
	}
	EEP_Erase(EEP_GetPage(EEP_RemainTry));//eeprom恢复为还剩1次机会
	EEP_Program(EEP_RemainTry + 1, 0);
}


uint8_t GetRemainTry() {
	//从EEPROM中读取并判断剩余解锁次数
	//方法详见eeprom.c
	uint16_t addr = EEP_RemainTry;
	uint8_t n = 0;
	while (EEP_Read(addr) != 0) n++;
	return n;
}

void main(void) using 0 {
	uint8_t remainTry;
	delay_Init();//启动延迟用计时器
	delay(0.5 * CLOCK_PER_SEC, false);//延迟0.5s
	LCD1602_Init();//LCD1602初始化序列
	LEDSPK_Init();//禁用LED，蜂鸣器
	KBD_Init();//键盘输出高电平
	LCD1602_CreateChar(1, GRAPH_KEYA);
	LCD1602_CreateChar(2, GRAPH_KEYB);//创建自定义字符
	remainTry = GetRemainTry();//从eeprom读取剩余次数
	if (remainTry == 0) {
		//次数为0立即禁止解锁
		BlockedState();
		remainTry = 1;
	}
	for (;;) {
		uint8_t ret;
		IdleState();//闲置
		ret = EnterPWDState();//输密码
		if (ret == 1) {
			//解锁成功
			if (remainTry != MAX_REMAIN_TRY) {
				//如果剩余次数不是满的，恢复剩余次数
				remainTry = MAX_REMAIN_TRY;
				EEP_Erase(EEP_GetPage(EEP_RemainTry));
				EEP_Program(EEP_RemainTry + MAX_REMAIN_TRY, 0);
			}
			LEDSPK_Flash(~(1 << LED_GREEN), SPK_FREQ_TO_RELOAD(TUNE_RIGHT), SPK_TIME_TO_CYCLE(TUNE_RIGHT, LEDSPK_TIME));
			LCD1602_PrintAll(STR_Unlocked, STR_Unlocked2);
			for (;;) {
				uint8_t key;
				while ((key = KBD_WaitKey(AUTOLOCK_TIME * CLOCK_PER_SEC)) < 0);
				if (key == KEY_A) {
					//A键锁定
					break;
				} else if (key == KEY_B) {
					//B键改密码
					ChangePWDState();
				} else if (key < 0) {
					//无操作自动锁定
					break;
				}
			}
			
		} else if (ret == 2) {
			//解锁失败
			//次数减一
			remainTry--;
			EEP_Program(EEP_RemainTry + remainTry, 0);
			LEDSPK_Flash(~(1 << LED_RED), SPK_FREQ_TO_RELOAD(TUNE_WRONG), SPK_TIME_TO_CYCLE(TUNE_WRONG, LEDSPK_TIME));
			//输出剩余次数
			LCD1602_PrintAll(STR_WrongPass, STR_RemaingTry);
			LCD1602_DDRamAddr(0x40 + 13);
			LCD1602_DataMode();
			LCD1602_Data('0' + remainTry);
			KBD_WaitKey(MESSAGE_TIME * CLOCK_PER_SEC);
			if (remainTry == 0) {
				//次数为零则禁止解锁
				BlockedState();
				remainTry = 1;
			}
		}
		//否则（ret == 0），用户取消解锁
	}
}









